**MAF1731B**: Microstructure and Trading Systems

**Laboratory 1**: Active vs Passive Investments

Professor: Villalobos Ramos Omar Antonio

Student: Márquez Delgado Esteban

Registry: if700637

September 2022 | Repository: Link


Laboratory 1

Active vs Passive Investing


Abstract

This document was prepared by Esteban Márquez Delgado as delivery for Laboratory 1 of Microstructure and Trading Systems - MAF1731B class during the Autumn 2022 course at ITESO for the Bachelor of Financial Engineering.

0. Libraries and Dependencies:

Note: To run this notebook it is necessary to have the following libraries contained in the requirements.txt script of this project.

To install the libraries in the script run the following cell:

In [1]:
!pip install -r requirements.txt
Requirement already satisfied: pandas>=1.3.4 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 10)) (1.4.2)
Requirement already satisfied: numpy>=1.19.1 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 11)) (1.21.5)
Requirement already satisfied: jupyter>=1.0.0 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 12)) (1.0.0)
Requirement already satisfied: chart_studio>=1.1 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 13)) (1.1.0)
Requirement already satisfied: plotly>=4.14 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 14)) (5.6.0)
Requirement already satisfied: pandas_datareader>=0.10.0 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 15)) (0.10.0)
Requirement already satisfied: tk>=0.1.0 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 16)) (0.1.0)
Requirement already satisfied: yahoofinancials>=1.6 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 18)) (1.6)
Requirement already satisfied: scipy>=1.7.3 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 19)) (1.7.3)
Requirement already satisfied: regex>=2022.3.15 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 20)) (2022.3.15)
Requirement already satisfied: matplotlib>=3.5.1 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 21)) (3.5.1)
Requirement already satisfied: fire>=0.4.0 in c:\programdata\anaconda3\lib\site-packages (from -r requirements.txt (line 22)) (0.4.0)
Requirement already satisfied: pytz>=2020.1 in c:\programdata\anaconda3\lib\site-packages (from pandas>=1.3.4->-r requirements.txt (line 10)) (2021.3)
Requirement already satisfied: python-dateutil>=2.8.1 in c:\programdata\anaconda3\lib\site-packages (from pandas>=1.3.4->-r requirements.txt (line 10)) (2.8.2)
Requirement already satisfied: notebook in c:\programdata\anaconda3\lib\site-packages (from jupyter>=1.0.0->-r requirements.txt (line 12)) (6.4.8)
Requirement already satisfied: ipywidgets in c:\programdata\anaconda3\lib\site-packages (from jupyter>=1.0.0->-r requirements.txt (line 12)) (7.6.5)
Requirement already satisfied: qtconsole in c:\programdata\anaconda3\lib\site-packages (from jupyter>=1.0.0->-r requirements.txt (line 12)) (5.3.0)
Requirement already satisfied: jupyter-console in c:\programdata\anaconda3\lib\site-packages (from jupyter>=1.0.0->-r requirements.txt (line 12)) (6.4.0)
Requirement already satisfied: ipykernel in c:\programdata\anaconda3\lib\site-packages (from jupyter>=1.0.0->-r requirements.txt (line 12)) (6.9.1)
Requirement already satisfied: nbconvert in c:\programdata\anaconda3\lib\site-packages (from jupyter>=1.0.0->-r requirements.txt (line 12)) (6.4.4)
Requirement already satisfied: retrying>=1.3.3 in c:\programdata\anaconda3\lib\site-packages (from chart_studio>=1.1->-r requirements.txt (line 13)) (1.3.3)
Requirement already satisfied: six in c:\programdata\anaconda3\lib\site-packages (from chart_studio>=1.1->-r requirements.txt (line 13)) (1.16.0)
Requirement already satisfied: requests in c:\programdata\anaconda3\lib\site-packages (from chart_studio>=1.1->-r requirements.txt (line 13)) (2.27.1)
Requirement already satisfied: tenacity>=6.2.0 in c:\programdata\anaconda3\lib\site-packages (from plotly>=4.14->-r requirements.txt (line 14)) (8.0.1)
Requirement already satisfied: lxml in c:\programdata\anaconda3\lib\site-packages (from pandas_datareader>=0.10.0->-r requirements.txt (line 15)) (4.8.0)
Requirement already satisfied: beautifulsoup4 in c:\programdata\anaconda3\lib\site-packages (from yahoofinancials>=1.6->-r requirements.txt (line 18)) (4.11.1)
Requirement already satisfied: pyparsing>=2.2.1 in c:\programdata\anaconda3\lib\site-packages (from matplotlib>=3.5.1->-r requirements.txt (line 21)) (3.0.4)
Requirement already satisfied: fonttools>=4.22.0 in c:\programdata\anaconda3\lib\site-packages (from matplotlib>=3.5.1->-r requirements.txt (line 21)) (4.25.0)
Requirement already satisfied: cycler>=0.10 in c:\programdata\anaconda3\lib\site-packages (from matplotlib>=3.5.1->-r requirements.txt (line 21)) (0.11.0)
Requirement already satisfied: kiwisolver>=1.0.1 in c:\programdata\anaconda3\lib\site-packages (from matplotlib>=3.5.1->-r requirements.txt (line 21)) (1.3.2)
Requirement already satisfied: pillow>=6.2.0 in c:\programdata\anaconda3\lib\site-packages (from matplotlib>=3.5.1->-r requirements.txt (line 21)) (9.0.1)
Requirement already satisfied: packaging>=20.0 in c:\programdata\anaconda3\lib\site-packages (from matplotlib>=3.5.1->-r requirements.txt (line 21)) (21.3)
Requirement already satisfied: termcolor in c:\programdata\anaconda3\lib\site-packages (from fire>=0.4.0->-r requirements.txt (line 22)) (1.1.0)
Requirement already satisfied: idna<4,>=2.5 in c:\programdata\anaconda3\lib\site-packages (from requests->chart_studio>=1.1->-r requirements.txt (line 13)) (3.3)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\programdata\anaconda3\lib\site-packages (from requests->chart_studio>=1.1->-r requirements.txt (line 13)) (1.26.9)
Requirement already satisfied: certifi>=2017.4.17 in c:\programdata\anaconda3\lib\site-packages (from requests->chart_studio>=1.1->-r requirements.txt (line 13)) (2021.10.8)
Requirement already satisfied: charset-normalizer~=2.0.0 in c:\programdata\anaconda3\lib\site-packages (from requests->chart_studio>=1.1->-r requirements.txt (line 13)) (2.0.4)
Requirement already satisfied: soupsieve>1.2 in c:\programdata\anaconda3\lib\site-packages (from beautifulsoup4->yahoofinancials>=1.6->-r requirements.txt (line 18)) (2.3.1)
Requirement already satisfied: matplotlib-inline<0.2.0,>=0.1.0 in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.1.2)
Requirement already satisfied: tornado<7.0,>=4.2 in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (6.1)
Requirement already satisfied: nest-asyncio in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (1.5.5)
Requirement already satisfied: traitlets<6.0,>=5.1.0 in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (5.1.1)
Requirement already satisfied: ipython>=7.23.1 in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (8.2.0)
Requirement already satisfied: jupyter-client<8.0 in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (6.1.12)
Requirement already satisfied: debugpy<2.0,>=1.0.0 in c:\programdata\anaconda3\lib\site-packages (from ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (1.5.1)
Requirement already satisfied: pygments>=2.4.0 in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.11.2)
Requirement already satisfied: backcall in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.2.0)
Requirement already satisfied: setuptools>=18.5 in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (61.2.0)
Requirement already satisfied: decorator in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (5.1.1)
Requirement already satisfied: pickleshare in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.7.5)
Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (3.0.20)
Requirement already satisfied: jedi>=0.16 in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.18.1)
Requirement already satisfied: stack-data in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.2.0)
Requirement already satisfied: colorama in c:\programdata\anaconda3\lib\site-packages (from ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.4.4)
Requirement already satisfied: parso<0.9.0,>=0.8.0 in c:\programdata\anaconda3\lib\site-packages (from jedi>=0.16->ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.8.3)
Requirement already satisfied: jupyter-core>=4.6.0 in c:\programdata\anaconda3\lib\site-packages (from jupyter-client<8.0->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (4.9.2)
Requirement already satisfied: pyzmq>=13 in c:\programdata\anaconda3\lib\site-packages (from jupyter-client<8.0->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (22.3.0)
Requirement already satisfied: pywin32>=1.0 in c:\programdata\anaconda3\lib\site-packages (from jupyter-core>=4.6.0->jupyter-client<8.0->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (302)
Requirement already satisfied: wcwidth in c:\programdata\anaconda3\lib\site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.2.5)
Requirement already satisfied: jupyterlab-widgets>=1.0.0 in c:\programdata\anaconda3\lib\site-packages (from ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (1.0.0)
Requirement already satisfied: nbformat>=4.2.0 in c:\programdata\anaconda3\lib\site-packages (from ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (5.3.0)
Requirement already satisfied: ipython-genutils~=0.2.0 in c:\programdata\anaconda3\lib\site-packages (from ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.2.0)
Requirement already satisfied: widgetsnbextension~=3.5.0 in c:\programdata\anaconda3\lib\site-packages (from ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (3.5.2)
Requirement already satisfied: fastjsonschema in c:\programdata\anaconda3\lib\site-packages (from nbformat>=4.2.0->ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.15.1)
Requirement already satisfied: jsonschema>=2.6 in c:\programdata\anaconda3\lib\site-packages (from nbformat>=4.2.0->ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (4.4.0)
Requirement already satisfied: importlib-resources>=1.4.0 in c:\programdata\anaconda3\lib\site-packages (from jsonschema>=2.6->nbformat>=4.2.0->ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (5.2.0)
Requirement already satisfied: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 in c:\programdata\anaconda3\lib\site-packages (from jsonschema>=2.6->nbformat>=4.2.0->ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.18.0)
Requirement already satisfied: attrs>=17.4.0 in c:\programdata\anaconda3\lib\site-packages (from jsonschema>=2.6->nbformat>=4.2.0->ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (21.4.0)
Requirement already satisfied: zipp>=3.1.0 in c:\programdata\anaconda3\lib\site-packages (from importlib-resources>=1.4.0->jsonschema>=2.6->nbformat>=4.2.0->ipywidgets->jupyter>=1.0.0->-r requirements.txt (line 12)) (3.7.0)
Requirement already satisfied: jinja2 in c:\programdata\anaconda3\lib\site-packages (from notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.11.3)
Requirement already satisfied: argon2-cffi in c:\programdata\anaconda3\lib\site-packages (from notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (21.3.0)
Requirement already satisfied: prometheus-client in c:\programdata\anaconda3\lib\site-packages (from notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.13.1)
Requirement already satisfied: Send2Trash>=1.8.0 in c:\programdata\anaconda3\lib\site-packages (from notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (1.8.0)
Requirement already satisfied: terminado>=0.8.3 in c:\programdata\anaconda3\lib\site-packages (from notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.13.1)
Requirement already satisfied: pywinpty>=1.1.0 in c:\programdata\anaconda3\lib\site-packages (from terminado>=0.8.3->notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.0.2)
Requirement already satisfied: argon2-cffi-bindings in c:\programdata\anaconda3\lib\site-packages (from argon2-cffi->notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (21.2.0)
Requirement already satisfied: cffi>=1.0.1 in c:\programdata\anaconda3\lib\site-packages (from argon2-cffi-bindings->argon2-cffi->notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (1.15.0)
Requirement already satisfied: pycparser in c:\programdata\anaconda3\lib\site-packages (from cffi>=1.0.1->argon2-cffi-bindings->argon2-cffi->notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.21)
Requirement already satisfied: MarkupSafe>=0.23 in c:\programdata\anaconda3\lib\site-packages (from jinja2->notebook->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.0.1)
Requirement already satisfied: jupyterlab-pygments in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.1.2)
Requirement already satisfied: defusedxml in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.7.1)
Requirement already satisfied: mistune<2,>=0.8.1 in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.8.4)
Requirement already satisfied: testpath in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.5.0)
Requirement already satisfied: bleach in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (4.1.0)
Requirement already satisfied: nbclient<0.6.0,>=0.5.0 in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.5.13)
Requirement already satisfied: entrypoints>=0.2.2 in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.4)
Requirement already satisfied: pandocfilters>=1.4.1 in c:\programdata\anaconda3\lib\site-packages (from nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (1.5.0)
Requirement already satisfied: webencodings in c:\programdata\anaconda3\lib\site-packages (from bleach->nbconvert->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.5.1)
Requirement already satisfied: qtpy>=2.0.1 in c:\programdata\anaconda3\lib\site-packages (from qtconsole->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.0.1)
Requirement already satisfied: executing in c:\programdata\anaconda3\lib\site-packages (from stack-data->ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.8.3)
Requirement already satisfied: pure-eval in c:\programdata\anaconda3\lib\site-packages (from stack-data->ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (0.2.2)
Requirement already satisfied: asttokens in c:\programdata\anaconda3\lib\site-packages (from stack-data->ipython>=7.23.1->ipykernel->jupyter>=1.0.0->-r requirements.txt (line 12)) (2.0.5)
WARNING: Ignoring invalid distribution -finance (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution - (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution -finance (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution - (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution -finance (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution - (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution -finance (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution - (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution -finance (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution - (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution -finance (c:\programdata\anaconda3\lib\site-packages)
WARNING: Ignoring invalid distribution - (c:\programdata\anaconda3\lib\site-packages)

If you prefer, manual installation can be done with the individual installation of the following libraries:

  • pandas>=1.3.4
  • numpy>=1.19.1
  • jupyter>=1.0.0
  • chart_studio>=1.1
  • plotly>=4.14
  • pandas_datareader>=0.10.0
  • tk>=0.10
  • plotly>=5.60
  • yahoofinancials>=1.60
  • scipy>=1.7.3
  • regex>=2022.3.15
  • matplotlib>=3.5.1
  • fire>=0.4.0

1. Introduction:

This document was prepared by Esteban Márquez Delgado as a delivery for Microstructure and Trading Systems - MAF1731B class during the Autumn 2022 course at ITESO for the Bachelor of Financial Engineering.

In the present work conceptual definitions of concern will be covered and evidence will be provided to answer the following question:

  • ¿What's the difference between Active and Passive investment with given data?

Data will be downloaded with YahooFinancials library according to provided csvs of NAFTRAC ETF holdings during periods of 2020/01/31 to 2022/07/29.

2. Concepts:

As of May 14, 2009 the name of the ETF covered in this paper changed from NAFTRAC to iShares NAFTRAC.

  • Inception Date: 2002/04/30
  • Expense Ratio: .25%
  • Benchmark: S&P/BMV IPC
  • Holdings: 35

The benchmark is comprised of the largest stock issuers in the Mexican market with the purpose of being a reliable indicator of the market and the ETF iShares NAFTRAC from BlackRock serves as a financial instrument to invest in the index S&P/BMV IPC.

Passive investment : Invest in NAFTRAC (ETF) with cash restrictions limited to ETF tickers present in period of portfolio conformation for the selected fund (except: KOFL.MX, KOFUBL.MX, USD.MXN, BSMXB.MX, NMKA.MX).

There are several known advantages from investing in an ETF, including the following:

  • Accesible Portfolio diversification.
  • Fast access to markets worlwide.
  • Reduced risks.
  • Easy Risk Management.
  • Reduced expenses.

    Active investing : Actively rebalance an EMV portfolio and a constantly restructured portfolio, limited to ETF tickers present in all periods for the selected fund according to data in monthly csvs (NAFTRAC_20200131.csv to NAFTRAC_20220729.csv (31) ).

The advantages from actively managing a portfolio, include the following:

  • Risk management.
  • Higher risks and returns (+/-).
  • Asset selection.

3. Objective:

The main objective is to evaluate the following metrics:

  • Capital
  • Returns
  • Accumulated Returns
  • Cash
  • Sharpe Ratio
  • Titles (Owned / Bought / Sold)
In [2]:
## Libraries
# Analysis and data management
import pandas as pd
import pandas_datareader as pdr
import numpy as np
import datetime
from tkinter.ttk import Style
from tkinter import Y
import plotly.graph_objects as go #plotly
import plotly.express as px
from yahoofinancials import YahooFinancials # Yfinance 
from scipy.optimize import minimize # Optimización 
from collections import Counter #Counter
import re
import io 
import glob
import os
import matplotlib.pyplot as plt # Visualization
import functions as fn
import visualizations as vs
import data as dt
import pandas as pd
from os import path
import fire
%matplotlib inline

Weights in Passive Portfolio consolidated in 2020-01-31 from NAFTRAC_20200131.csv

Note: KOFUBL.MX, BSMXB.MX, NMKA.MX and MXN positions are dropped from the dataframe and considered as cash.

In [3]:
help(dt.read_csv)
Help on function read_csv in module data:

read_csv(data)
    Function that reads csv files and returns a dataframe of its content.
    
        Parameters
        ----------
        data: csv data.
    
        Returns
        -------
        data: pd.DataFrame(data)

In [4]:
t=dt.read_csv('NAFTRAC_20200131.csv')[['Ticker','Peso (%)']].dropna().drop(10).drop(34).drop(32) #Read tickers and weights of first csv (port. consolidation).
W=(t['Peso (%)']/100).values #1-W.sum() Cash
t
Out[4]:
Ticker Peso (%)
0 AMXL 13.70
1 FEMSAUBD 11.85
2 GFNORTEO 10.64
3 WALMEX* 10.39
4 GMEXICOB 6.03
5 CEMEXCPO 4.30
6 TLEVISACPO 3.96
7 GAPB 3.29
8 ELEKTRA* 3.02
9 ASURB 2.75
11 KIMBERA 2.06
12 BIMBOA 1.87
13 OMAB 1.85
14 AC* 1.77
15 GFINBURO 1.76
16 IENOVA* 1.73
17 PINFRA* 1.68
18 GRUMAB 1.68
19 ORBIA* 1.67
20 ALFAA 1.51
21 GCARSOA1 1.16
22 PE&OLES* 0.91
23 ALSEA* 0.86
24 BBAJIOO 0.83
25 GENTERA* 0.81
26 MEGACPO 0.79
27 LIVEPOLC.1 0.74
28 BOLSAA 0.72
29 CUERVO* 0.67
30 LABB 0.62
31 GCC* 0.59
33 RA 0.44
35 ALPEKA 0.25

In order, to retrieve specific help from the scripts in the project execute the help function on the script or in specific modules from the following:

  • data.py
  • functions.py
  • visualizations.py
In [5]:
help(vs.hist_csv)
Help on function hist_csv in module visualizations:

hist_csv(df, title, tickers, weights)
    Function that returns histogram of tickers and weights of portfolio in a df.
    
        Parameters
        ----------
        df: Tickers and Weights of stocks in a dataframe.
        title: Title of the histogram.
        tickers: Column with tickers as str.
        weights: Column with tickers as str.
    
        Returns
        -------
        histogram of Tickers and Weights of the portfolio in a df.

In [6]:
vs.hist_csv(t, "Weights of Passive Portfolio (consolidation)",'Ticker','Peso (%)')
In [8]:
start = datetime.datetime(2020,1,31) #Portfolio conformation.
end = datetime.datetime(2022,7,29) #End of investment.
print('Download data and show summary of Passive Investment Portfolio from period', start.strftime("%Y-%m-%d"), 'to', end.strftime("%Y-%m-%d"))
Download data and show summary of Passive Investment Portfolio from period 2020-01-31 to 2022-07-29
In [9]:
capital = 1000000
com = -.00125
tickers=[stocks.replace('*','') for stocks in t['Ticker']+'.MX'] #Replace values to get readable tickers.
tickers[26]='LIVEPOLC-1.MX' #Replace a single value.
tickers
Out[9]:
['AMXL.MX',
 'FEMSAUBD.MX',
 'GFNORTEO.MX',
 'WALMEX.MX',
 'GMEXICOB.MX',
 'CEMEXCPO.MX',
 'TLEVISACPO.MX',
 'GAPB.MX',
 'ELEKTRA.MX',
 'ASURB.MX',
 'KIMBERA.MX',
 'BIMBOA.MX',
 'OMAB.MX',
 'AC.MX',
 'GFINBURO.MX',
 'IENOVA.MX',
 'PINFRA.MX',
 'GRUMAB.MX',
 'ORBIA.MX',
 'ALFAA.MX',
 'GCARSOA1.MX',
 'PE&OLES.MX',
 'ALSEA.MX',
 'BBAJIOO.MX',
 'GENTERA.MX',
 'MEGACPO.MX',
 'LIVEPOLC-1.MX',
 'BOLSAA.MX',
 'CUERVO.MX',
 'LABB.MX',
 'GCC.MX',
 'RA.MX',
 'ALPEKA.MX']
In [10]:
help(dt.yf_adjclose)
Help on function yf_adjclose in module data:

yf_adjclose(tickers, start, end)
    Function that downloads and returns yahoo finance ticker(s) adj. closes.
    
        Parameters
        ----------
        tickers: ticker(s) to download as a list.
        start: datetime.datetime(y,m,d)
        end: datetime.datetime(y,m,d)
    
        Returns
        -------
        data: pd.DataFrame(data)

In [12]:
df=dt.yf_adjclose(tickers, start, end)
s0=df.head(1)
df.head()
Out[12]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX IENOVA.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX GENTERA.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX GCC.MX RA.MX ALPEKA.MX
Date
2020-01-31 14.346073 160.300339 105.844643 53.542744 42.370079 7.595 41.303806 216.240250 1366.929688 339.708923 34.365952 32.136036 122.502434 94.615448 21.424999 88.714996 188.090439 186.317108 40.384415 14.103790 66.101830 197.247894 45.825001 25.000610 20.633625 63.203465 92.458412 37.607738 33.807873 20.180393 95.766121 93.203430 17.603239
2020-02-04 14.615718 164.075867 106.049728 54.905067 45.631260 7.945 41.653214 220.965332 1364.359619 342.187866 34.286758 32.592335 123.128777 96.169991 22.600000 90.334999 188.348450 189.490524 42.516857 14.093850 67.263809 199.836685 45.849998 25.088739 20.736549 63.529854 91.039772 37.529819 34.795952 20.270689 96.513840 95.576775 17.429491
2020-02-05 14.524314 165.276321 105.789963 54.415405 44.998440 7.750 40.304802 222.125824 1360.484375 348.642487 34.207573 32.302391 122.753815 94.924583 22.240000 89.300003 186.841797 188.706436 42.621429 13.954700 64.905807 196.098434 46.240002 24.996605 21.094330 65.170746 89.495926 37.088291 34.708775 19.963692 95.384933 94.390106 17.295456
2020-02-06 14.332363 165.596451 105.871994 53.533047 44.213726 7.750 39.733936 221.940887 1348.394287 345.808105 34.269169 31.494360 123.580414 95.057083 21.400000 89.089996 188.058167 188.363144 42.257687 13.577009 63.982059 192.969894 46.099998 24.940521 20.829670 63.248177 86.992424 37.460564 33.556007 19.774078 91.485085 92.448273 17.166386
2020-02-07 14.295801 165.935410 103.894180 54.299053 43.741215 7.790 39.399296 223.771759 1375.075562 343.095245 33.952423 31.532379 122.762329 95.048256 22.040001 89.260002 186.758850 189.291031 41.930321 13.278831 64.137634 193.109833 45.200001 24.628065 20.623825 64.339119 87.159332 37.235466 32.761665 18.925327 94.319565 94.062141 16.967815
In [13]:
St=df.tail(1)
df.tail()
Out[13]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX IENOVA.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX GENTERA.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX GCC.MX RA.MX ALPEKA.MX
Date
2022-07-25 18.560059 121.089996 116.169998 71.860001 77.099358 8.31 32.240002 281.720001 1158.920044 382.739990 28.870001 74.239998 124.489998 139.020004 35.070000 73.699997 140.424179 251.630005 43.820000 14.10 76.989998 189.699997 38.910000 41.854561 16.309999 48.049999 89.320000 35.490002 45.610672 19.370001 118.410004 106.199997 26.900000
2022-07-26 18.394083 122.389999 114.300003 70.760002 74.858444 8.00 30.760000 270.549988 1172.030029 379.760010 28.870001 74.070000 120.959999 143.910004 34.950001 73.699997 139.952774 247.820007 43.470001 13.59 76.980003 187.960007 38.290001 42.102928 16.530001 47.689999 89.160004 36.000000 45.987213 18.709999 118.940002 105.669998 25.959999
2022-07-27 18.325741 124.250000 113.010002 71.459999 74.354973 8.03 31.190001 272.540009 1175.290039 380.459991 28.900000 73.070000 120.919998 140.130005 35.150002 73.699997 141.268814 248.479996 42.299999 14.03 77.959999 193.059998 38.580002 43.225185 17.150000 48.200001 89.589996 37.380001 46.700653 19.400000 120.930000 109.220001 27.219999
2022-07-28 18.892012 126.820000 117.650002 73.599998 77.632439 8.13 32.270000 278.489990 1184.459961 384.769989 29.299999 75.080002 124.400002 141.410004 37.430000 73.699997 142.604507 253.419998 43.939999 14.23 79.949997 211.360001 39.189999 43.777111 17.030001 47.779999 91.269997 37.590000 46.918648 19.320000 123.040001 111.230003 26.650000
2022-07-29 18.940828 127.029999 115.989998 73.839996 79.567329 8.23 32.139999 276.399994 1197.390015 384.000000 30.020000 72.040001 124.169998 141.210007 37.540001 73.699997 144.411606 253.490005 44.959999 14.01 79.510002 205.080002 39.660000 44.099068 16.570000 47.410000 91.480003 37.939999 45.719669 18.950001 125.989998 111.370003 27.110001
In [14]:
help(vs.stocks_summary)
Help on function stocks_summary in module visualizations:

stocks_summary(s0, St, W, capital, returns, mean_ret)
    Function that returns a stock summary of the behavior of a portfolio in a given date 
    with known initial global investment, returns of assets, weights and S0 and St.
    
        Parameters
        ----------
        s0: Initial price in a dataframe of 1*n.
        St: Last price in a dataframe of 1*n.
        W: Weights of holdings as an array.
        capital: Scalar of capital amount.
        returns: Dataframe of holdings returns.
        mean_ret: Mean returns of holdings.
    
        Returns
        -------
        A dataframe with the behavior of Holdings (S_0, S_t, Owned Titles, Initial_investment, Return and Volatility in a given period).

In [15]:
returns, mean_ret, cov= fn.s_metrics(df)[0], fn.s_metrics(df)[1], fn.s_metrics(df)[2]
stocks_summary=vs.stocks_summary(s0,St, W, capital, returns, mean_ret)
stocks_summary
Out[15]:
S_0 S_t Rounded_Titles Initial_Investment Return Volatility
Symbols
AMXL.MX 14.346073 18.940828 9550.0 137004.998589 0.148598 0.273509
FEMSAUBD.MX 160.300339 127.029999 739.0 118461.950333 -0.052876 0.284635
GFNORTEO.MX 105.844643 115.989998 1005.0 106373.865852 0.126355 0.423030
WALMEX.MX 53.542744 73.839996 1941.0 103926.465488 0.167182 0.278108
GMEXICOB.MX 42.370079 79.567329 1423.0 60292.622475 0.332085 0.400770
CEMEXCPO.MX 7.595000 8.230000 5662.0 43002.888812 0.141860 0.470635
TLEVISACPO.MX 41.303806 32.139999 959.0 39610.350246 0.012703 0.479625
GAPB.MX 216.240250 276.399994 152.0 32868.517944 0.188951 0.423914
ELEKTRA.MX 1366.929688 1197.390015 22.0 30072.453125 -0.040061 0.161066
ASURB.MX 339.708923 384.000000 81.0 27516.422791 0.114147 0.361635
KIMBERA.MX 34.365952 30.020000 599.0 20585.204971 -0.006264 0.310031
BIMBOA.MX 32.136036 72.040001 582.0 18703.172905 0.398483 0.389774
OMAB.MX 122.502434 124.169998 151.0 18497.867500 0.092444 0.417460
AC.MX 94.615448 141.210007 187.0 17693.088776 0.188716 0.239398
GFINBURO.MX 21.424999 37.540001 821.0 17589.924374 0.303244 0.396569
IENOVA.MX 88.714996 73.699997 195.0 17299.424286 -0.034846 0.279192
PINFRA.MX 188.090439 144.411606 89.0 16740.049057 -0.063772 0.289097
GRUMAB.MX 186.317108 253.490005 90.0 16768.539734 0.165898 0.292974
ORBIA.MX 40.384415 44.959999 414.0 16719.147675 0.112671 0.372697
ALFAA.MX 14.103790 14.010000 1071.0 15105.159393 0.108199 0.471404
GCARSOA1.MX 66.101830 79.510002 175.0 11567.820168 0.165908 0.429436
PE&OLES.MX 197.247894 205.080002 46.0 9073.403137 0.125442 0.471795
ALSEA.MX 45.825001 39.660000 188.0 8615.100143 0.066328 0.485726
BBAJIOO.MX 25.000610 44.099068 332.0 8300.202637 0.300099 0.381890
GENTERA.MX 20.633625 16.570000 393.0 8109.014637 0.047251 0.522969
MEGACPO.MX 63.203465 47.410000 125.0 7900.433064 -0.073475 0.289133
LIVEPOLC-1.MX 92.458412 91.480003 80.0 7396.672974 0.061739 0.363073
BOLSAA.MX 37.607738 37.939999 191.0 7183.078053 0.067481 0.360801
CUERVO.MX 33.807873 45.719669 198.0 6693.958809 0.168834 0.311243
LABB.MX 20.180393 18.950001 307.0 6195.380718 0.039878 0.360957
GCC.MX 95.766121 125.989998 62.0 5937.499496 0.157187 0.308055
RA.MX 93.203430 111.370003 47.0 4380.561218 0.163949 0.428491
ALPEKA.MX 17.603239 27.110001 142.0 2499.659946 0.234068 0.350800
In [16]:
pd.set_option('display.float_format', lambda x: '%.4f' % x)

I_inv = stocks_summary.Initial_Investment.sum()
cash = capital-I_inv

passive_summary = pd.DataFrame({'Portfolio_IV':I_inv+cash, "Cash": cash, 'Commision':I_inv*com}, index=['Passive_Summary'])
passive_summary
Out[16]:
Portfolio_IV Cash Commision
Passive_Summary 1000000.0000 31315.1007 -1210.8561
In [17]:
print("The Passive Investment portfolio started with a capital of", I_inv.round(2), '+ cash')
The Passive Investment portfolio started with a capital of 968684.9 + cash

The returns of the assets composing the ETF and the portfolio are calculated on a daily basis in order to get the accumulated returns for each day on the dataframe shown below and to plot the behavior of the portfolio more precisely.

In [18]:
port_pas=vs.port_pasivo(returns, W, capital, I_inv, com, cash)
port_pas.head()
Out[18]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX IENOVA.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX GENTERA.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX GCC.MX RA.MX ALPEKA.MX Capital Portfolio Returns Accumulated Returns
timestamp
2020-01-31 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 998789.1439 -0.0012 0.9988
2020-02-04 2575.0133 2791.0113 206.1618 2643.5970 4641.2282 1981.5690 334.9939 718.9004 -56.7813 200.6745 -47.4708 265.5209 94.5887 290.8130 965.2285 315.9110 23.0452 286.1433 881.8201 -10.6423 203.9121 119.4335 4.6913 29.2581 40.4043 40.7964 -113.5423 -14.9176 195.8162 27.7415 46.0658 112.0422 -24.6756 1018557.4959 0.0198 1.0186
2020-02-05 -856.7723 867.0007 -260.6236 -926.6157 -836.2481 -1055.3816 -1281.9443 172.7881 -85.7782 518.7269 -47.5758 -166.3565 -56.3377 -229.2161 -280.3545 -198.2115 -134.3880 -69.5163 41.0745 -149.0845 -406.6499 -170.2294 73.1522 -30.4805 139.7543 204.0465 -125.4887 -84.7061 -16.7861 -93.8983 -69.0113 -54.6298 -19.2253 1012868.5290 -0.0056 1.0129
2020-02-06 -1810.5681 229.5267 82.5043 -1684.7629 -1051.5529 0.0000 -560.8830 -27.3917 -268.3755 -223.5685 37.0934 -467.7728 124.5752 24.7064 -664.7483 -40.6844 109.3707 -30.5623 -142.5223 -408.6884 -165.0927 -145.1807 -26.0387 -18.6223 -101.6266 -233.0539 -207.0029 72.2698 -222.5241 -58.8870 -241.2237 -90.5187 -18.6567 1004638.0663 -0.0081 1.0046
2020-02-07 -349.4880 242.5572 -1987.6775 1486.7092 -644.4250 221.9353 -333.5125 271.4041 597.5808 -215.7371 -190.4033 22.5742 -122.4674 -1.6437 526.3562 33.0127 -116.0732 82.7577 -129.3732 -331.6266 28.2058 6.5992 -167.8955 -103.9828 -80.0466 136.2639 14.1980 -43.2642 -158.6032 -266.1189 182.7996 76.8108 -28.9185 1003296.5736 -0.0013 1.0033
In [19]:
port_pas.tail()
Out[19]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX IENOVA.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX GENTERA.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX GCC.MX RA.MX ALPEKA.MX Capital Portfolio Returns Accumulated Returns
timestamp
2022-07-25 -1355.7434 -497.0004 2445.4392 -2013.3435 -138.6580 470.8037 -365.0882 -353.5147 208.0710 87.9378 -494.4551 -50.2430 286.7284 121.7857 151.8547 0.0000 -86.5020 143.4232 -158.5450 -273.3976 -37.5453 -185.1581 236.1766 95.9542 -170.1683 -19.6801 -9.9287 36.7035 100.4631 -82.1187 -10.4451 102.1677 -0.9290 1260881.9258 -0.0014 1.2609
2022-07-26 -1225.1391 1272.1973 -1712.7269 -1590.4514 -1752.6356 -1604.0935 -1817.8677 -1304.4634 341.6297 -214.1126 0.0000 -42.8201 -524.5801 622.5938 -60.2219 0.0000 -56.3977 -254.3733 -133.3860 -546.1704 -1.5059 -83.4682 -137.0339 49.2527 109.2587 -59.1884 -13.2554 103.4654 55.3122 -211.2551 26.4082 -21.9585 -87.3606 1250007.5778 -0.0086 1.2500
2022-07-27 -509.0160 1800.8830 -1200.8407 1027.8361 -405.5564 161.2486 553.5765 241.9948 84.0015 50.6886 21.4054 -252.4639 -6.1179 -464.9154 100.7157 0.0000 157.9781 44.7414 -449.4832 488.8884 147.6741 246.9138 65.1347 221.2372 303.8107 84.4835 35.6880 276.0002 103.9430 228.6480 98.7135 147.8188 121.3406 1253534.5484 0.0028 1.2535
2022-07-28 4233.3406 2451.0661 4368.6039 3111.4741 2657.9418 535.4940 1371.2086 718.2593 235.6283 311.5306 285.1208 514.3977 532.4186 161.6783 1141.6210 0.0000 158.8436 333.9989 647.4702 215.2528 296.1003 862.5817 135.9765 105.9795 -56.6759 -68.8385 138.7655 40.4493 31.2750 -25.5670 102.9439 80.9743 -52.3512 1279111.5111 0.0204 1.2791
2022-07-29 354.0060 196.2221 -1501.2698 338.8013 1502.9015 528.9023 -159.5303 -246.9061 329.6757 -55.0321 506.2125 -757.1659 -34.2047 -25.0332 51.7235 0.0000 212.8913 4.6410 387.6652 -233.4497 -63.8391 -270.3822 103.1388 61.0419 -218.7908 -61.1760 17.0270 67.0388 -171.2146 -118.7367 141.4579 5.5380 43.1521 1280046.8167 0.0007 1.2800

A summary of the capital behavior, its monthly and accumulated returns is shown on the next dataframe.

In [20]:
df_pasiva=vs.df_pasiva(port_pas)
df_pasiva['timestamp']=df_pasiva.index
df_pasiva[['timestamp', 'Capital', 'Portfolio Returns', 'Accumulated Returns']].reset_index(drop=True)
Out[20]:
Symbols timestamp Capital Portfolio Returns Accumulated Returns
0 2020-01-31 998789.1439 -0.0012 0.9988
1 2020-02-28 937043.2657 -0.0058 0.9370
2 2020-03-31 767564.7940 0.0147 0.7676
3 2020-04-30 833851.1775 -0.0109 0.8339
4 2020-05-29 839549.0464 -0.0129 0.8395
5 2020-06-30 895077.4327 -0.0010 0.8951
6 2020-07-31 879866.4674 -0.0028 0.8799
7 2020-08-31 881342.5886 -0.0273 0.8813
8 2020-09-30 900998.5648 0.0089 0.9010
9 2020-10-30 892971.2885 0.0046 0.8930
10 2020-11-30 1037201.5549 0.0020 1.0372
11 2020-12-31 1089432.9928 -0.0116 1.0894
12 2021-01-29 1059641.4580 -0.0266 1.0596
13 2021-02-26 1092550.5699 0.0066 1.0926
14 2021-03-31 1152784.9473 -0.0135 1.1528
15 2021-04-30 1186455.5197 -0.0123 1.1865
16 2021-05-31 1252749.9830 0.0131 1.2527
17 2021-06-30 1245206.2836 -0.0010 1.2452
18 2021-07-30 1263823.1701 -0.0090 1.2638
19 2021-08-31 1311200.5377 0.0098 1.3112
20 2021-09-30 1286457.8696 0.0050 1.2865
21 2021-10-29 1290072.3425 0.0008 1.2901
22 2021-11-30 1264996.0019 -0.0018 1.2650
23 2021-12-31 1335490.4062 0.0016 1.3355
24 2022-01-31 1306098.0059 0.0094 1.3061
25 2022-02-28 1345925.0703 0.0119 1.3459
26 2022-03-31 1403510.5447 0.0079 1.4035
27 2022-04-29 1322158.2000 -0.0154 1.3222
28 2022-05-31 1342524.9296 -0.0049 1.3425
29 2022-06-30 1269600.5959 -0.0076 1.2696
30 2022-07-29 1280046.8167 0.0007 1.2800

The following plot shows the behavior of the portfolio during the investment period.

In [21]:
help(vs.plot)
Help on function plot in module visualizations:

plot(metric, title, xlabel, ylabel)
    Function that converts the capital in a portfolio, its returns and accumulated returns to a monthly basis.
    
        Parameters
        ----------
        port_pas: Dataframe that contains 'Capital', 'Portfolio Returns' and 'Accumulated Returns' columns in order.
    
        Returns
        -------
        A monthly dataframe of the Capital the Monthly Return and Accumulated Return for the investment in a period.

In [20]:
vs.plot(port_pas.Capital, 'Passive Investment (Daily)', 'Date', 'Capital')
In [22]:
help(vs.plotly_graph)
Help on function plotly_graph in module visualizations:

plotly_graph(x, y, x_label, y_label, title)
    Function that plots a line+marker graph with plotly.
    
        Parameters
        ----------
        x: index from Dataframe of selected metric to graph with plotly.
        y: Values of the selected of selected metric to graph with plotly.
        title: Title of the plot.
        x_label: Variable name in the label x.
        y_label: Variable name in the label y.
    
        Returns
        -------
        Returns a didactic graph with plotly of the selected metric.

In [23]:
vs.plotly_graph(x=df_pasiva['timestamp'], y=df_pasiva['Capital'], title="Passive Investment Capital", x_label='Dates', y_label="Capital")
In [24]:
vs.plotly_graph(x=df_pasiva['timestamp'], y=df_pasiva['Portfolio Returns'], title="Passive Investment Returns", x_label='Dates', y_label="Capital")

Ticker selection from constant re appearance in CSVs.

In [25]:
pd.options.mode.chained_assignment = None
extension = 'csv' 
all_filenames = [i for i in glob.glob('*.{}'.format(extension))] #read all csvs in one line
combined_csv = pd.concat([pd.read_csv(f) for f in all_filenames ]).dropna() #concatenate in a df
tick_rep  = Counter(combined_csv['Ticker']) #count occurences
tick_sorted = sorted(tick_rep.items(), key=lambda kv: kv[1]) #sort first values of tuple by occurence
print("ticker_list", str(tick_sorted)) #print all strings of tuples despite number of occurrence
ticker_selection = [i for i in tick_sorted if i[1] >= 31] #List compression to filter tickers by max n° of occurence
#ticker_selection[24] = ('LIVEPOLC-1','31') #Rename LIVEPOLC.1 to downloadable data
ticker_selection #results
ticker_list [('USD', 2), ('ALPEKA', 3), ('NMKA', 3), ('SITES1A-1', 5), ('BSMXB', 8), ('VOLARA', 11), ('GENTERA*', 14), ('IENOVA*', 16), ('SITESB.1', 18), ('RA', 25), ('GCC*', 26), ('VESTA*', 28), ('Q*', 28), ('AMXL', 31), ('FEMSAUBD', 31), ('GFNORTEO', 31), ('WALMEX*', 31), ('GMEXICOB', 31), ('CEMEXCPO', 31), ('TLEVISACPO', 31), ('GAPB', 31), ('ELEKTRA*', 31), ('ASURB', 31), ('KOFUBL', 31), ('KIMBERA', 31), ('BIMBOA', 31), ('OMAB', 31), ('AC*', 31), ('GFINBURO', 31), ('PINFRA*', 31), ('GRUMAB', 31), ('ORBIA*', 31), ('ALFAA', 31), ('GCARSOA1', 31), ('PE&OLES*', 31), ('ALSEA*', 31), ('BBAJIOO', 31), ('MEGACPO', 31), ('LIVEPOLC.1', 31), ('BOLSAA', 31), ('CUERVO*', 31), ('LABB', 31), ('MXN', 31)]
Out[25]:
[('AMXL', 31),
 ('FEMSAUBD', 31),
 ('GFNORTEO', 31),
 ('WALMEX*', 31),
 ('GMEXICOB', 31),
 ('CEMEXCPO', 31),
 ('TLEVISACPO', 31),
 ('GAPB', 31),
 ('ELEKTRA*', 31),
 ('ASURB', 31),
 ('KOFUBL', 31),
 ('KIMBERA', 31),
 ('BIMBOA', 31),
 ('OMAB', 31),
 ('AC*', 31),
 ('GFINBURO', 31),
 ('PINFRA*', 31),
 ('GRUMAB', 31),
 ('ORBIA*', 31),
 ('ALFAA', 31),
 ('GCARSOA1', 31),
 ('PE&OLES*', 31),
 ('ALSEA*', 31),
 ('BBAJIOO', 31),
 ('MEGACPO', 31),
 ('LIVEPOLC.1', 31),
 ('BOLSAA', 31),
 ('CUERVO*', 31),
 ('LABB', 31),
 ('MXN', 31)]

For not consolidating NAFTRAC in all periods IENOVA.MX, GENTERA.MX, GCC.MX, RA.MX AND ALPEKA.MX should be removed from the ticker selection as well as the tickers KOFL.MX, KOFUBL.MX, USD.MXN, BSMXB.MX, NMKA.MX (2. Concepts section).

In [26]:
t2= pd.read_csv('NAFTRAC_20200131.csv')[['Ticker','Peso (%)']].drop(10).drop(16).drop(25).drop(31).drop(32).drop(33).drop(34).drop(35).drop(36)
W2=t2['Peso (%)']/100 #1-W2.sum() Cash
t2.Ticker[27]='LIVEPOLC-1'
tickers_ap=[stocks.replace('*','') for stocks in t2.Ticker + '.MX']
tickers_ap
Out[26]:
['AMXL.MX',
 'FEMSAUBD.MX',
 'GFNORTEO.MX',
 'WALMEX.MX',
 'GMEXICOB.MX',
 'CEMEXCPO.MX',
 'TLEVISACPO.MX',
 'GAPB.MX',
 'ELEKTRA.MX',
 'ASURB.MX',
 'KIMBERA.MX',
 'BIMBOA.MX',
 'OMAB.MX',
 'AC.MX',
 'GFINBURO.MX',
 'PINFRA.MX',
 'GRUMAB.MX',
 'ORBIA.MX',
 'ALFAA.MX',
 'GCARSOA1.MX',
 'PE&OLES.MX',
 'ALSEA.MX',
 'BBAJIOO.MX',
 'MEGACPO.MX',
 'LIVEPOLC-1.MX',
 'BOLSAA.MX',
 'CUERVO.MX',
 'LABB.MX']
In [27]:
start = datetime.datetime(2020,1,31) #Portfolio conformation.
end = datetime.datetime(2021,1,31) #End of investment.
print('Download data and show summary of Active Investment Portfolio (EMV) from period', start.strftime("%Y-%m-%d"), 'to', end.strftime("%Y-%m-%d"))
Download data and show summary of Active Investment Portfolio (EMV) from period 2020-01-31 to 2021-01-31
In [28]:
df2=dt.yf_adjclose(tickers_ap, start, end)
s0=df2.head(1)
df2.head()
Out[28]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX
Date
2020-01-31 14.3461 160.3003 105.8447 53.5427 42.3701 7.5950 41.3038 216.2402 1366.9298 339.7089 34.3659 32.1360 122.5024 94.6155 21.4250 188.0904 186.3171 40.3844 14.1038 66.1018 197.2479 45.8250 25.0006 63.2035 92.4584 37.6077 33.8079 20.1804
2020-02-04 14.6157 164.0759 106.0497 54.9051 45.6313 7.9450 41.6532 220.9653 1364.3595 342.1879 34.2868 32.5923 123.1288 96.1700 22.6000 188.3484 189.4905 42.5169 14.0939 67.2638 199.8367 45.8500 25.0887 63.5299 91.0398 37.5298 34.7960 20.2707
2020-02-05 14.5243 165.2763 105.7900 54.4154 44.9984 7.7500 40.3048 222.1258 1360.4844 348.6425 34.2076 32.3024 122.7538 94.9246 22.2400 186.8418 188.7065 42.6214 13.9547 64.9058 196.0984 46.2400 24.9966 65.1707 89.4959 37.0883 34.7088 19.9637
2020-02-06 14.3324 165.5965 105.8720 53.5331 44.2137 7.7500 39.7339 221.9409 1348.3944 345.8081 34.2692 31.4944 123.5804 95.0571 21.4000 188.0582 188.3631 42.2577 13.5770 63.9821 192.9699 46.1000 24.9405 63.2482 86.9924 37.4606 33.5560 19.7741
2020-02-07 14.2958 165.9354 103.8942 54.2990 43.7412 7.7900 39.3993 223.7717 1375.0756 343.0952 33.9524 31.5324 122.7623 95.0482 22.0400 186.7589 189.2910 41.9303 13.2788 64.1376 193.1098 45.2000 24.6281 64.3391 87.1593 37.2355 32.7617 18.9253
In [29]:
St=df2.tail(1)
df2.tail()
Out[29]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX
Date
2021-01-25 13.9326 144.7866 96.4022 60.6645 82.4846 12.4300 34.2123 193.2389 1388.7820 301.1777 33.6193 40.7484 103.1456 89.3091 19.0700 155.4610 222.3233 43.7959 13.4238 59.5189 316.2923 22.9800 20.0293 69.2293 63.1121 42.3578 46.6492 19.8102
2021-01-26 13.7800 143.6113 96.0103 60.7338 81.2241 12.3800 34.9800 197.6681 1382.7607 307.9223 34.5690 40.4880 103.4438 89.2812 19.3400 154.6244 221.7306 44.6861 13.7425 58.2548 321.4899 22.7800 19.8931 69.4681 63.2373 42.2585 46.1875 19.7470
2021-01-27 13.5702 143.0429 94.5885 59.4860 81.1258 11.8400 32.5194 191.8519 1384.5959 307.2955 34.1080 39.0413 101.1771 87.9604 19.0100 149.6233 219.6945 44.1788 13.7723 56.0183 299.4502 22.5700 19.6207 69.4865 64.1428 42.1049 44.2721 20.0901
2021-01-28 13.3986 141.3377 97.0220 59.4266 81.2420 12.1700 32.5785 192.0923 1378.2570 303.9747 33.8037 38.7037 102.4639 88.3139 18.9200 147.0182 218.9585 44.1213 13.6230 53.9180 300.7696 22.9300 19.1641 70.7267 63.5552 41.6894 42.9656 20.0540
2021-01-29 13.0362 134.5844 92.5742 57.7828 78.5244 11.7600 30.5017 191.0382 1374.9238 301.7109 33.0199 37.1510 103.1030 86.5653 18.2800 146.4762 215.1349 42.2929 12.8462 50.1646 306.6068 23.1100 19.8370 68.0902 63.1025 41.5539 42.9558 18.9524
In [49]:
log_ret, mean_lr, cov= fn.log_metrics(df2)[1], fn.log_metrics(df2)[2], fn.log_metrics(df2)[4]
stocks_summary2=vs.stocks_summary(s0,St, W2.values.flatten(), capital, log_ret, mean_lr)
stocks_summary2
Out[49]:
S_0 S_t Rounded_Titles Initial_Investment Return Volatility
Symbols
AMXL.MX 14.3461 13.0362 9550.0000 137004.9895 -0.0961 0.3322
FEMSAUBD.MX 160.3003 134.5844 739.0000 118461.9503 -0.1756 0.3518
GFNORTEO.MX 105.8447 92.5742 1005.0000 106373.8735 -0.1345 0.5051
WALMEX.MX 53.5427 57.7828 1941.0000 103926.4655 0.0765 0.2923
GMEXICOB.MX 42.3701 78.5244 1423.0000 60292.6279 0.6194 0.4185
CEMEXCPO.MX 7.5950 11.7600 5662.0000 43002.8888 0.4390 0.5539
TLEVISACPO.MX 41.3038 30.5017 959.0000 39610.3502 -0.3044 0.5762
GAPB.MX 216.2402 191.0382 152.0000 32868.5179 -0.1244 0.5715
ELEKTRA.MX 1366.9298 1374.9238 22.0000 30072.4558 0.0059 0.1346
ASURB.MX 339.7089 301.7109 81.0000 27516.4228 -0.1191 0.4683
KIMBERA.MX 34.3659 33.0199 599.0000 20585.2004 -0.0401 0.3544
BIMBOA.MX 32.1360 37.1510 582.0000 18703.1729 0.1456 0.4416
OMAB.MX 122.5024 103.1030 151.0000 18497.8675 -0.1731 0.5701
AC.MX 94.6155 86.5653 187.0000 17693.0902 -0.0893 0.2685
GFINBURO.MX 21.4250 18.2800 821.0000 17589.9244 -0.1594 0.5122
PINFRA.MX 188.0904 146.4762 89.0000 16740.0477 -0.2511 0.3759
GRUMAB.MX 186.3171 215.1349 90.0000 16768.5397 0.1444 0.3517
ORBIA.MX 40.3844 42.2929 414.0000 16719.1477 0.0464 0.4947
ALFAA.MX 14.1038 12.8462 1071.0000 15105.1584 -0.0938 0.6596
GCARSOA1.MX 66.1018 50.1646 175.0000 11567.8202 -0.2770 0.5190
PE&OLES.MX 197.2479 306.6068 46.0000 9073.4031 0.4429 0.5891
ALSEA.MX 45.8250 23.1100 188.0000 8615.1001 -0.6873 0.6886
BBAJIOO.MX 25.0006 19.8370 332.0000 8300.2026 -0.2323 0.4701
MEGACPO.MX 63.2035 68.0902 125.0000 7900.4326 0.0748 0.3491
LIVEPOLC-1.MX 92.4584 63.1025 80.0000 7396.6748 -0.3835 0.4678
BOLSAA.MX 37.6077 41.5539 191.0000 7183.0781 0.1002 0.4644
CUERVO.MX 33.8079 42.9558 198.0000 6693.9588 0.2404 0.3602
LABB.MX 20.1804 18.9524 307.0000 6195.3813 -0.0630 0.4490
In [55]:
rf = 6.95 / (100) # Treasury Yield Curve Rates 1Yr (2021-12-31) 
opt=fn.optimize(mean_lr)
w0, bnds, cons, N = opt[0], opt[1], opt[2], len(mean_lr)
In [56]:
s0_pemv=s0[['GMEXICOB.MX', 'CEMEXCPO.MX', 'PE&OLES.MX', 'CUERVO.MX']]
frames=[w_pemv,s0]
metrics=pd.concat(frames)[['GMEXICOB.MX','CEMEXCPO.MX','PE&OLES.MX','CUERVO.MX']]
metrics
Out[56]:
Symbols GMEXICOB.MX CEMEXCPO.MX PE&OLES.MX CUERVO.MX
Weights 0.7545 0.0725 0.0774 0.0956
2020-01-31 00:00:00 42.3701 7.5950 197.2479 33.8079
In [57]:
# Variance minimization function
def Var(w, cov):
    return np.dot(w.T, np.dot(cov, w))

# EMV Minimization function
def Minus_RatioSharpe(w, er, rf, cov):
    erp = np.dot(w.T, er)
    sp = np.dot(w.T, np.dot(cov, w))**0.5
    RS = (erp - rf) / sp
    return -RS

pmv = minimize(fun = Var, x0 = w0, args = (cov,), bounds = bnds, constraints = cons, tol = 1e-10)
pemv = minimize(fun = Minus_RatioSharpe, x0 = w0, args = (mean_lr, rf, cov), bounds = bnds, constraints = cons, tol = 1e-10)

#Weights of EMV
w_pemv = pd.DataFrame(np.round(pemv.x.reshape(1, N), 4), columns = log_ret.columns, index = ["Weights"])
w_pemv[w_pemv <= 0.0] = np.nan
w_pemv.dropna(axis = 1, inplace = True)
In [62]:
# Annualized metrics of Passive Investment Portfolio.
Er_pemv = np.dot(pemv.x.T, mean_lr)
s_pemv = (np.dot(pemv.x.T, np.dot(cov, pemv.x)))**0.5

pemv_summary = pd.DataFrame({"Return" : Er_pemv, "Volatility" : s_pemv,
                            "Sharpe Ratio" : (Er_pemv - rf) / s_pemv}, index = ["EMV Portafolio"])
pemv_summary
Out[62]:
Return Volatility Sharpe Ratio
EMV Portafolio 0.5564 0.3649 1.3345
In [65]:
plt.style.use('dark_background')
# Minimum Variance Frontier
w = np.linspace(0, 1, 100)
Er_pmv = np.dot(pmv.x, mean_lr/100)
s_pmv = (np.dot(pmv.x.T, np.dot(cov, pmv.x)))**0.5
cov_pmv_pemv = np.dot(pmv.x.T, np.dot(cov, pemv.x))

plt.figure(figsize = (18, 10))

minvar_frontier = pd.DataFrame({"Volatilidad" : ((w*s_pemv)**2 + 2*w*(1-w)*cov_pmv_pemv + ((1-w)*s_pmv)**2)**0.5, 
                                "Rendimiento" : (w*(Er_pemv/100))/100 + (1 - w)*(Er_pemv/100)})
minvar_frontier["Sharpe Ratio"] = (minvar_frontier["Rendimiento"] - rf) / minvar_frontier["Volatilidad"]

plt.scatter(minvar_frontier["Volatilidad"], (minvar_frontier["Rendimiento"])[::-1]*100,
            c = minvar_frontier["Sharpe Ratio"], cmap = "coolwarm")

# ACL
sp = np.linspace(0, 0.5)
lac = pd.DataFrame({"Volatilidad" : sp, "Rendimiento" : pemv_summary["Sharpe Ratio"].values[0]*sp + rf})
plt.plot(lac["Volatilidad"], lac["Rendimiento"], "--", color = "white", label = "Línea de asignación de capital")

# Minimum Variance Frontier
plt.plot(s_pemv, (Er_pemv), "*r", ms=16, label = ("EMV:", 'E(r)=',(Er_pemv).round(2),'σ=',s_pemv.round(2)))

# EMV
plt.plot(s_pmv, (Er_pemv/100), "*b", ms=13, color = "dodgerblue",
         label = ("Port. min. var.", 'E(r)=',(Er_pemv).round(2),'σ=',s_pmv.round(2)))

# Individual Assets
for i in range(len(stocks_summary2)):
    if stocks_summary2.index[i] in w_pemv.columns:
        plt.plot(stocks_summary2.iloc[i, 5], stocks_summary2.iloc[i, 4], "*", ms=10, label=('E(r)=',
        stocks_summary2.iloc[i, 4].round(2),'σ=',stocks_summary2.iloc[i, 5].round(2)))

        plt.text(stocks_summary2.iloc[i, 5] + 0.003, stocks_summary2.iloc[i, 4], stocks_summary2.index[i])
#Style
plt.title("E(r) vs σ for Active Investment EMV from " + str(start.strftime("%Y-%m-%d"))+ ' to ' + str(end.strftime("%Y-%m-%d")),size='17', weight='bold', family="Constantia")
plt.xlabel("Volatility (annualized) $\sigma$",size='15', weight='roman', family="Georgia", color='r')
plt.ylabel("Expected Return (annualized) $E[r]$",size='15', weight='roman', family="Georgia", color='g')
plt.grid(True)
plt.legend(loc = "best")
C:\Users\Esteban\AppData\Local\Temp\ipykernel_49884\3011666208.py:26: UserWarning:

color is redundantly defined by the 'color' keyword argument and the fmt string "*b" (-> color='b'). The keyword argument will take precedence.

Out[65]:
<matplotlib.legend.Legend at 0x19e4257dc10>
In [66]:
#Dates
start = datetime.datetime(2021,1,31) #Portfolio conformation.
end = datetime.datetime(2022,7,29) #Portfolio EMV maximization end date.

#Daily Adj. closes df 
df3 = pdr.DataReader(tickers_ap, 'yahoo',start,end)["Adj Close"] #Adj. closes download
# Mean, Std, and Covariance from returns.
returns3 = df3.pct_change().fillna(0) #NAs filled w/ 0s to preserve daily returns for all rows (days) in every column (ticker)
mean_ret3 = returns3.mean() * 252 
cov3 = returns.cov() * 252
stocks_summary3 = pd.DataFrame({"Rendimiento" : mean_ret3, "Volatilidad" : returns3.std()*np.sqrt(252)})
stocks_summary3
Out[66]:
Rendimiento Volatilidad
Symbols
AMXL.MX 0.2729 0.2273
FEMSAUBD.MX -0.0073 0.2269
GFNORTEO.MX 0.2042 0.3631
WALMEX.MX 0.1852 0.2683
GMEXICOB.MX 0.0561 0.3861
CEMEXCPO.MX -0.1998 0.3985
TLEVISACPO.MX 0.0881 0.4008
GAPB.MX 0.2717 0.2960
ELEKTRA.MX -0.0804 0.1761
ASURB.MX 0.1678 0.2652
KIMBERA.MX -0.0293 0.2759
BIMBOA.MX 0.4748 0.3484
OMAB.MX 0.1556 0.2751
AC.MX 0.3396 0.2169
GFINBURO.MX 0.4970 0.2986
PINFRA.MX 0.0089 0.2157
GRUMAB.MX 0.1246 0.2455
ORBIA.MX 0.0463 0.2650
ALFAA.MX 0.1437 0.2815
GCARSOA1.MX 0.3614 0.3600
PE&OLES.MX -0.2511 0.3599
ALSEA.MX 0.3977 0.3340
BBAJIOO.MX 0.5850 0.3111
MEGACPO.MX -0.2448 0.2361
LIVEPOLC-1.MX 0.2903 0.2749
BOLSAA.MX -0.0406 0.2615
CUERVO.MX 0.0622 0.2716
LABB.MX 0.0317 0.2913
In [67]:
pd.set_option('display.float_format', lambda x: '%.6f' % x)
a_cumsum=returns3.iloc[returns3.cumprod().reset_index().groupby(returns3.index.to_period('M'))['Date'].idxmax()]+1
BS=a_cumsum.apply(lambda x: ['SELL' if y <= .95 else "BUY" if y>=1.05 else 'HOLD' if y > .95 and  y<1.05 else y for y in x])
BS = BS.style.applymap(lambda x: 'color: red' if x == 'SELL' else 'color: green' if x == 'BUY' else 'color: gray')
BS
Out[67]:
Symbols AMXL.MX FEMSAUBD.MX GFNORTEO.MX WALMEX.MX GMEXICOB.MX CEMEXCPO.MX TLEVISACPO.MX GAPB.MX ELEKTRA.MX ASURB.MX KIMBERA.MX BIMBOA.MX OMAB.MX AC.MX GFINBURO.MX PINFRA.MX GRUMAB.MX ORBIA.MX ALFAA.MX GCARSOA1.MX PE&OLES.MX ALSEA.MX BBAJIOO.MX MEGACPO.MX LIVEPOLC-1.MX BOLSAA.MX CUERVO.MX LABB.MX
Date                                                        
2021-02-26 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD
2021-03-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD SELL HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-04-30 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-05-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-06-30 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-07-30 00:00:00 HOLD HOLD HOLD HOLD SELL HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-08-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD
2021-09-30 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-10-29 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD SELL HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-11-30 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2021-12-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2022-01-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD
2022-02-28 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2022-03-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2022-04-29 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD
2022-05-31 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD BUY SELL HOLD HOLD
2022-06-30 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD SELL HOLD BUY HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
2022-07-29 00:00:00 HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD HOLD
In [68]:
# Annualized metrics of Passive Investment Portfolio.
Er_pemv = np.dot(pemv.x.T, mean_lr)
s_pemv = (np.dot(pemv.x.T, np.dot(cov, pemv.x)))**0.5

pemv_summary = pd.DataFrame({"Return" : Er_pemv, "Volatility" : s_pemv,
                            "Sharpe Ratio" : (Er_pemv - rf) / s_pemv}, index = ["EMV Portafolio"])
pemv_summary
Out[68]:
Return Volatility Sharpe Ratio
EMV Portafolio 0.556439 0.364886 1.334497
In [69]:
pas_s =(((W*stocks_summary.Volatility).sum()*np.sqrt(252))/np.sqrt(df.shape[0]))
pas_r = df_pasiva['Accumulated Returns'][-1]-1

# Annualized metrics of EMV.
pemv_summary = pd.DataFrame({"Return" : pas_r, "Volatility" : pas_s,
                            "Sharpe Ratio" : (pas_r - rf) / pas_s}, index = ["Passive Portfolio"])
pemv_summary
Out[69]:
Return Volatility Sharpe Ratio
Passive Portfolio 0.280047 0.211970 0.993284
In [71]:
df_activa=log_ret[['GMEXICOB.MX', 'CEMEXCPO.MX', 'PE&OLES.MX', 'CUERVO.MX']]*w_pemv.values.flatten()*capital
df_activa["Capital"] = df_activa.sum(axis=1)
df_activa["Capital"][0] = capital*com+capital
df_activa["Capital"]=df_activa["Capital"].cumsum()

df_activa["Portfolio Returns"] = df_activa["Capital"].pct_change()
df_activa["Portfolio Returns"][0] = (capital*com)/capital #Charged on investment day (0).

df_activa["Accumulated Returns"] = (df_activa["Portfolio Returns"] + 1).cumprod()
df_activa.index.name="timestamp"
df_activa.round(6)

df_activa.head()
Out[71]:
Symbols GMEXICOB.MX CEMEXCPO.MX PE&OLES.MX CUERVO.MX Capital Portfolio Returns Accumulated Returns
timestamp
2020-01-31 0.000000 0.000000 0.000000 0.000000 998750.000000 -0.001250 0.998750
2020-02-04 55946.604781 3266.321705 1009.232111 2753.983238 1061726.141835 0.063055 1.061726
2020-02-05 -10536.861661 -1801.623422 -1461.599087 -239.825911 1047686.231754 -0.013224 1.047686
2020-02-06 -13273.371962 0.000000 -1244.790184 -3229.034295 1029939.035313 -0.016939 1.029939
2020-02-07 -8106.919641 373.230842 56.108769 -2290.271294 1019971.183990 -0.009678 1.019971
In [72]:
df_activa.tail()
Out[72]:
Symbols GMEXICOB.MX CEMEXCPO.MX PE&OLES.MX CUERVO.MX Capital Portfolio Returns Accumulated Returns
timestamp
2021-01-25 17035.729631 526.850194 107.703236 -3011.894099 1604417.870348 0.009221 1.604418
2021-01-26 -11618.634506 -292.222384 1261.561923 -950.854183 1592817.721198 -0.007230 1.592818
2021-01-27 -914.000738 -3233.400976 -5496.802086 -4049.248743 1579124.268655 -0.008597 1.579124
2021-01-28 1079.960737 1993.044642 340.280353 -2863.578483 1579673.975903 0.000348 1.579674
2021-01-29 -25670.446525 -2484.571472 1487.773978 -21.867168 1552984.864716 -0.016895 1.552985
In [73]:
vs.df_pasiva(df_activa)
Out[73]:
Symbols Capital Portfolio Returns Accumulated Returns
timestamp
2020-01-31 998750.000000 -0.001250 0.998750
2020-02-28 903578.287129 0.021652 0.903578
2020-03-31 844520.937752 0.080129 0.844521
2020-04-30 1004274.235493 -0.007500 1.004274
2020-05-29 966343.565545 -0.022892 0.966344
2020-06-30 1093862.975148 0.009924 1.093863
2020-07-31 1163432.563285 -0.003492 1.163433
2020-08-31 1205868.311979 -0.004944 1.205868
2020-09-30 1196047.678058 0.020156 1.196048
2020-10-30 1245464.069608 -0.003294 1.245464
2020-11-30 1404143.313426 0.013732 1.404143
2020-12-31 1530437.270727 -0.014844 1.530437
2021-01-29 1552984.864716 -0.016895 1.552985

According to the metrics and the behavior of the portfolios it can be concluded that an actively managed portfolio has a much bigger expected return but has in fact a higher volatility which is to be expected but even in times of recession it could very well be worth the risks with a higher sharpe ratio as a measure of risk-reward relation. The theory backed up with this paper confirms that it should be opt for risk management rather than passive investment strategies even when drawbacks are expected in the markets.

8. Bibliography:

Active vs. passive investing — the great investment debate. (2022). Retrieved 8 September 2022, from https://www.rathbones.com/sites/rathbones.com/files/literature/pdfs/rathbones_active_vs_passive_investing_james_pettit_investment_report_full_website.pdf

Grupo BMV - Tipos de Índices. (2022). Retrieved 8 September 2022, from https://www.bmv.com.mx/es/Grupo_BMV/Tipos_de_indices

iShares NAFTRAC | NAFTRAC. (2022). Retrieved 8 September 2022, from https://www.blackrock.com/mx/intermediarios/productos/251895/ishares-naftrac-fund

Villalobos, O, 2021. Python Project Template. https://github.com/OmarVillalobos/python-project.